<?php

namespace src;

use src\database\DatabaseManager;
use src\providers\RoutingServiceProvider;
use src\providers\ServiceProvider;

class Application extends Container
{
    protected $basePath;
    protected $appPath;

    //指示应用程序以前是否已引导。
    protected $hasBeenBootstrapped = false;
    protected $booted = false;

    protected $serviceProviders = [];

    // 启动前回调监听
    protected $bootingCallbacks = [];

    // 启动后回调监听
    protected $bootedCallbacks = [];

    public function __construct($basePath = '')
    {
        $this->instance(self::class, $this);
        if ($basePath) {
            $this->setBasePath($basePath);
        }
        $this->registerBaseProvider();
        $this->registerCoreContainerAliases();
    }

    /**
     * 设置应用路径
     * @param $basePath
     * @return $this
     */
    protected function setBasePath($basePath)
    {
        $this->basePath = rtrim($basePath, '\/');
        $this->bindPathsInContainer();
        return $this;
    }

    public function basePath($path = '')
    {
        return $this->basePath.($path ? DIRECTORY_SEPARATOR.$path : $path);
    }

    protected function bindPathsInContainer()
    {
        $this->instance('path', $this->path());
        $this->instance('config', $this->configPath());
    }

    public function path($path = '')
    {
        $appPath = $this->appPath ?: $this->basePath.DIRECTORY_SEPARATOR.'app';
        return $appPath.($path ? DIRECTORY_SEPARATOR.$path : $path);
    }
    //获取配置文件路径
    public function configPath(): string
    {
        return $this->basePath . DIRECTORY_SEPARATOR . "config";
    }

    /*
     * 注册基础提供者
     */
    protected function registerBaseProvider()
    {
        $this->register(new RoutingServiceProvider($this));
    }

    public function registerCoreContainerAliases()
    {
        //注册服务别名
        foreach ([
                     'router' => [Router::class],
                     'db' => [DatabaseManager::class]
                 ] as $key => $aliases) {
            foreach ($aliases as $alias) {
                $this->alias($key, $alias);
            }
        }
    }

    /**
     * 注册服务提供者
     * @param ServiceProvider | string $provider
     * @param $force
     * @return void
     */
    public function register($provider, $force = false)
    {
        $providerName = is_string($provider) ? $provider : get_class($provider);
        if ($registed = $this->getProvider($providerName)) {
            return $registed;
        }
        if (is_string($provider)) {
            $provider = $this->resolveProvider($provider);
        }
        $provider->register();
        if (property_exists($provider, 'bindings')) {
            foreach ($provider->bindings as $key => $value) {
                $this->bind($key, $value);
            }
        }
        if (property_exists($provider, 'singletons')) {
            foreach ($provider->singletons as $key => $value) {
                $this->singleton($key, $value);
            }
        }
        $this->serviceProviders[$providerName] = $provider;
    }

    protected function resolveProvider($provider)
    {
        return new $provider($this);
    }

    protected function getProvider($name)
    {
        return $this->serviceProviders[$name] ?? null;
    }

    /**
     * 确认程序是否已经引导
     * @return bool|mixed
     */
    public function hasBeenBootstrapped()
    {
        return $this->hasBeenBootstrapped;
    }

    /**
     * 运行引导类数组
     * @param array $bootstrappers
     * @return void
     */
    public function bootstrapWith(array $bootstrappers)
    {
        $this->hasBeenBootstrapped = true;
        foreach ($bootstrappers as $bootstrapper) {
            $this->make($bootstrapper)->bootstrap($this);
        }
    }

    public function isBooted()
    {
        return $this->booted;
    }

    //服务提供者启动前 绑定回调前监听
    public function booting($callback)
    {
        $this->bootingCallbacks[] = $callback;
    }

    //服务提供者启动行为后callback 如果服务提供者已经全部启动 则立刻执行回调
    public function booted($callback)
    {
        $this->bootedCallbacks[] = $callback;
        if ($this->isBooted()) {
            $callback($this);
        }
    }

    /**
     * 启用应用程序服务提供者
     * @return void
     */
    public function boot()
    {
        if ($this->isBooted()) {
            return;
        }
        //启动前回调
        $this->fireAppCallbacks($this->bootingCallbacks);
        array_walk($this->serviceProviders, function ($p) {
            $this->bootProvider($p);
        });
        $this->booted = true;
        //启动后回调
        $this->fireAppCallbacks($this->bootedCallbacks);
    }

    protected function bootProvider(ServiceProvider $provider)
    {
        $provider->callBootingCallbacks();
        if (method_exists($provider, 'boot')) {
            $provider->boot();
        }
        $provider->callBootedCallbacks();
    }


    /**
     * 执行callback 数组回调
     * @param array $callbacks
     * @return void
     */
    protected function fireAppCallbacks(array $callbacks)
    {
        $index = 0;
        while ($index < count($callbacks)) {
            $callbacks[$index]($this);
            $index++;
        }
    }

    //注册配置文件的providers
    public function registerConfiguredProviders()
    {
        foreach ($this['config']->get('app.providers') as $item){
            $this->register(new $item($this));
        }
    }
}